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 399cdc56fc..97ccd881f7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -1379,29 +1379,8 @@ public class ConversationFragment extends LoggingFragment implements Multiselect } private void postMarkAsReadRequest() { - if (getListAdapter().hasNoConversationMessages()) { - return; - } - - int position = getListLayoutManager().findFirstVisibleItemPosition(); - if (position == -1 || position == getListAdapter().getItemCount() - 1) { - return; - } - - ConversationMessage item = getListAdapter().getItem(position); - if (item == null) { - item = getListAdapter().getItem(position + 1); - } - - if (item != null) { - MessageRecord record = item.getMessageRecord(); - long latestReactionReceived = Stream.of(record.getReactions()) - .map(ReactionRecord::getDateReceived) - .max(Long::compareTo) - .orElse(0L); - - conversationViewModel.submitMarkReadRequest(Math.max(record.getDateReceived(), latestReactionReceived)); - } + Optional timestamp = MarkReadHelper.getLatestTimestamp(Objects.requireNonNull(getListAdapter()), getListLayoutManager()); + timestamp.ifPresent(conversationViewModel::submitMarkReadRequest); } private void updateToolbarDependentMargins() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/MarkReadHelper.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/MarkReadHelper.java index 50f4060138..4c7eb0f660 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/MarkReadHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/MarkReadHelper.java @@ -6,11 +6,16 @@ import androidx.annotation.NonNull; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; +import com.annimon.stream.Stream; + import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; +import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager; import org.thoughtcrime.securesms.database.MessageTable; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.database.ThreadTable; +import org.thoughtcrime.securesms.database.model.MessageRecord; +import org.thoughtcrime.securesms.database.model.ReactionRecord; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.notifications.MarkReadReceiver; import org.thoughtcrime.securesms.notifications.v2.ConversationId; @@ -18,6 +23,7 @@ import org.thoughtcrime.securesms.util.Debouncer; import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor; import java.util.List; +import java.util.Optional; import java.util.concurrent.Executor; public class MarkReadHelper { @@ -57,4 +63,42 @@ public class MarkReadHelper { }); }); } + + /** + * Given the adapter and manager, figure out the timestamp to mark read up to. + * + * @param conversationAdapter The conversation thread's adapter + * @param layoutManager The conversation thread's layout manager + * @return A Present(Long) if there's a timestamp to proceed with, or Empty if this request should be ignored. + */ + @SuppressWarnings("resource") + public static @NonNull Optional getLatestTimestamp(@NonNull ConversationAdapter conversationAdapter, + @NonNull SmoothScrollingLinearLayoutManager layoutManager) + { + if (conversationAdapter.hasNoConversationMessages()) { + return Optional.empty(); + } + + int position = layoutManager.findFirstVisibleItemPosition(); + if (position == -1 || position == layoutManager.getItemCount() - 1) { + return Optional.empty(); + } + + ConversationMessage item = conversationAdapter.getItem(position); + if (item == null) { + item = conversationAdapter.getItem(position + 1); + } + + if (item != null) { + MessageRecord record = item.getMessageRecord(); + long latestReactionReceived = Stream.of(record.getReactions()) + .map(ReactionRecord::getDateReceived) + .max(Long::compareTo) + .orElse(0L); + + return Optional.of(Math.max(record.getDateReceived(), latestReactionReceived)); + } + + return Optional.empty(); + } }