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 a9aebf5d1..b7fdedb1b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -217,6 +217,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect private Animation mentionButtonOutAnimation; private OnScrollListener conversationScrollListener; private int pulsePosition = -1; + private int lastSeenScrollOffset; private View toolbarShadow; private Stopwatch startupStopwatch; @@ -365,7 +366,10 @@ public class ConversationFragment extends LoggingFragment implements Multiselect getViewLifecycleOwner().getLifecycle().addObserver(conversationUpdateTick); listener.getVoiceNoteMediaController().getVoiceNotePlayerViewState().observe(getViewLifecycleOwner(), state -> conversationViewModel.setInlinePlayerVisible(state.isPresent())); - conversationViewModel.getScrollDateTopMargin().observe(getViewLifecycleOwner(), topMargin -> ViewUtil.setTopMargin(scrollDateHeader, topMargin)); + conversationViewModel.getConversationTopMargin().observe(getViewLifecycleOwner(), topMargin -> { + lastSeenScrollOffset = topMargin; + ViewUtil.setTopMargin(scrollDateHeader, topMargin + ViewUtil.dpToPx(8)); + }); return view; } @@ -1101,7 +1105,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect .submit(); } else if (conversation.getMessageRequestData().isMessageRequestAccepted()) { snapToTopDataObserver.buildScrollPosition(conversation.shouldScrollToLastSeen() ? lastSeenPosition : lastScrolledPosition) - .withOnPerformScroll((layoutManager, position) -> layoutManager.scrollToPositionWithOffset(position, list.getHeight())) + .withOnPerformScroll((layoutManager, position) -> layoutManager.scrollToPositionWithOffset(position, list.getHeight() - (conversation.shouldScrollToLastSeen() ? lastSeenScrollOffset : 0))) .withOnScrollRequestComplete(afterScroll) .submit(); } else { @@ -1247,7 +1251,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect public void onGlobalLayout() { Rect rect = new Rect(); toolbar.getGlobalVisibleRect(rect); - conversationViewModel.setToolbarBottom(rect.bottom + ViewUtil.dpToPx(8)); + conversationViewModel.setToolbarBottom(rect.bottom); ViewUtil.setTopMargin(conversationBanner, rect.bottom + ViewUtil.dpToPx(16)); toolbar.getViewTreeObserver().removeOnGlobalLayoutListener(this); } 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 0e906931b..2a184d949 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationViewModel.java @@ -75,7 +75,7 @@ public class ConversationViewModel extends ViewModel { private final LiveData chatColors; private final MutableLiveData toolbarBottom; private final MutableLiveData inlinePlayerHeight; - private final LiveData scrollDateTopMargin; + private final LiveData conversationTopMargin; private final Map> sessionMemberCache = new HashMap<>(); @@ -98,7 +98,7 @@ public class ConversationViewModel extends ViewModel { 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)); + this.conversationTopMargin = Transformations.distinctUntilChanged(LiveDataUtil.combineLatest(toolbarBottom, inlinePlayerHeight, Integer::sum)); LiveData recipientLiveData = LiveDataUtil.mapAsync(recipientId, Recipient::resolved); LiveData threadAndRecipient = LiveDataUtil.combineLatest(threadId, recipientLiveData, ThreadAndRecipient::new); @@ -161,11 +161,11 @@ public class ConversationViewModel extends ViewModel { } void setToolbarBottom(int bottom) { - toolbarBottom.postValue(bottom); + toolbarBottom.setValue(bottom); } void setInlinePlayerVisible(boolean isVisible) { - inlinePlayerHeight.postValue(isVisible ? ViewUtil.dpToPx(36) : 0); + inlinePlayerHeight.setValue(isVisible ? ViewUtil.dpToPx(36) : 0); } void onAttachmentKeyboardOpen() { @@ -186,8 +186,8 @@ public class ConversationViewModel extends ViewModel { this.threadId.postValue(-1L); } - @NonNull LiveData getScrollDateTopMargin() { - return scrollDateTopMargin; + @NonNull LiveData getConversationTopMargin() { + return conversationTopMargin; } @NonNull LiveData canShowAsBubble() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/MessageCountsViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/MessageCountsViewModel.java index 11381dd17..0c95be112 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/MessageCountsViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/MessageCountsViewModel.java @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.conversation; import android.app.Application; import android.content.Context; -import android.database.ContentObserver; import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; @@ -11,10 +10,9 @@ import androidx.lifecycle.Transformations; import androidx.lifecycle.ViewModel; import org.signal.core.util.concurrent.SignalExecutors; -import org.thoughtcrime.securesms.database.DatabaseContentProviders; import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessageDatabase; -import org.thoughtcrime.securesms.database.MmsSmsDatabase; +import org.thoughtcrime.securesms.database.DatabaseObserver; +import org.thoughtcrime.securesms.database.model.ThreadRecord; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor; import org.whispersystems.libsignal.util.Pair; @@ -26,10 +24,10 @@ public class MessageCountsViewModel extends ViewModel { private static final Executor EXECUTOR = new SerialMonoLifoExecutor(SignalExecutors.BOUNDED); private final Application context; - private final MutableLiveData threadId = new MutableLiveData<>(-1L); + private final MutableLiveData threadId = new MutableLiveData<>(-1L); private final LiveData> unreadCounts; - private ContentObserver observer; + private DatabaseObserver.Observer observer; public MessageCountsViewModel() { this.context = ApplicationDependencies.getApplication(); @@ -41,18 +39,24 @@ public class MessageCountsViewModel extends ViewModel { return counts; } - observer = new ContentObserver(null) { + observer = new DatabaseObserver.Observer() { + private int previousUnreadCount = -1; + @Override - public void onChange(boolean selfChange) { + public void onChanged() { EXECUTOR.execute(() -> { - counts.postValue(getCounts(context, id)); + int unreadCount = getUnreadCount(context, id); + if (unreadCount != previousUnreadCount) { + previousUnreadCount = unreadCount; + counts.postValue(new Pair<>(unreadCount, getUnreadMentionsCount(context, id))); + } }); } }; - observer.onChange(false); + observer.onChanged(); - context.getContentResolver().registerContentObserver(DatabaseContentProviders.Conversation.getUriForThread(id), true, observer); + ApplicationDependencies.getDatabaseObserver().registerConversationListObserver(observer); return counts; }); @@ -75,19 +79,19 @@ public class MessageCountsViewModel extends ViewModel { return Transformations.map(unreadCounts, Pair::second); } - private Pair getCounts(@NonNull Context context, long threadId) { - MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context); - MessageDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); - int unreadCount = mmsSmsDatabase.getUnreadCount(threadId); - int unreadMentionCount = mmsDatabase.getUnreadMentionCount(threadId); + private int getUnreadCount(@NonNull Context context, long threadId) { + ThreadRecord threadRecord = DatabaseFactory.getThreadDatabase(context).getThreadRecord(threadId); + return threadRecord != null ? threadRecord.getUnreadCount() : 0; + } - return new Pair<>(unreadCount, unreadMentionCount); + private int getUnreadMentionsCount(@NonNull Context context, long threadId) { + return DatabaseFactory.getMmsDatabase(context).getUnreadMentionCount(threadId); } @Override protected void onCleared() { if (observer != null) { - context.getContentResolver().unregisterContentObserver(observer); + ApplicationDependencies.getDatabaseObserver().unregisterObserver(observer); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java index 8de965487..da8b6d5c2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java @@ -256,13 +256,6 @@ public class MmsSmsDatabase extends Database { } } - public Cursor getUnread() { - String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " ASC"; - String selection = MmsSmsColumns.NOTIFIED + " = 0 AND (" + MmsSmsColumns.READ + " = 0 OR " + MmsSmsColumns.REACTIONS_UNREAD + " = 1)"; - - return queryTables(PROJECTION, selection, order, null); - } - public Cursor getMessagesForNotificationState(Collection stickyThreads) { StringBuilder stickyQuery = new StringBuilder(); for (MessageNotifierV2.StickyThread stickyThread : stickyThreads) { @@ -286,13 +279,13 @@ public class MmsSmsDatabase extends Database { } public int getUnreadCount(long threadId) { - String selection = MmsSmsColumns.READ + " = 0 AND " + MmsSmsColumns.NOTIFIED + " = 0 AND " + MmsSmsColumns.THREAD_ID + " = " + threadId; + String selection = MmsSmsColumns.READ + " = 0 AND " + MmsSmsColumns.THREAD_ID + " = " + threadId; Cursor cursor = queryTables(PROJECTION, selection, null, null); try { return cursor != null ? cursor.getCount() : 0; } finally { - if (cursor != null) cursor.close();; + if (cursor != null) cursor.close(); } }