kopia lustrzana https://github.com/ryukoposting/Signal-Android
Fix various bugs around unread counts and scroll to bottom.
rodzic
3310246351
commit
76f52b9086
|
@ -217,6 +217,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
||||||
private Animation mentionButtonOutAnimation;
|
private Animation mentionButtonOutAnimation;
|
||||||
private OnScrollListener conversationScrollListener;
|
private OnScrollListener conversationScrollListener;
|
||||||
private int pulsePosition = -1;
|
private int pulsePosition = -1;
|
||||||
|
private int lastSeenScrollOffset;
|
||||||
private View toolbarShadow;
|
private View toolbarShadow;
|
||||||
private Stopwatch startupStopwatch;
|
private Stopwatch startupStopwatch;
|
||||||
|
|
||||||
|
@ -365,7 +366,10 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
||||||
getViewLifecycleOwner().getLifecycle().addObserver(conversationUpdateTick);
|
getViewLifecycleOwner().getLifecycle().addObserver(conversationUpdateTick);
|
||||||
|
|
||||||
listener.getVoiceNoteMediaController().getVoiceNotePlayerViewState().observe(getViewLifecycleOwner(), state -> conversationViewModel.setInlinePlayerVisible(state.isPresent()));
|
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;
|
return view;
|
||||||
}
|
}
|
||||||
|
@ -1101,7 +1105,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
||||||
.submit();
|
.submit();
|
||||||
} else if (conversation.getMessageRequestData().isMessageRequestAccepted()) {
|
} else if (conversation.getMessageRequestData().isMessageRequestAccepted()) {
|
||||||
snapToTopDataObserver.buildScrollPosition(conversation.shouldScrollToLastSeen() ? lastSeenPosition : lastScrolledPosition)
|
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)
|
.withOnScrollRequestComplete(afterScroll)
|
||||||
.submit();
|
.submit();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1247,7 +1251,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
||||||
public void onGlobalLayout() {
|
public void onGlobalLayout() {
|
||||||
Rect rect = new Rect();
|
Rect rect = new Rect();
|
||||||
toolbar.getGlobalVisibleRect(rect);
|
toolbar.getGlobalVisibleRect(rect);
|
||||||
conversationViewModel.setToolbarBottom(rect.bottom + ViewUtil.dpToPx(8));
|
conversationViewModel.setToolbarBottom(rect.bottom);
|
||||||
ViewUtil.setTopMargin(conversationBanner, rect.bottom + ViewUtil.dpToPx(16));
|
ViewUtil.setTopMargin(conversationBanner, rect.bottom + ViewUtil.dpToPx(16));
|
||||||
toolbar.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
toolbar.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class ConversationViewModel extends ViewModel {
|
||||||
private final LiveData<ChatColors> chatColors;
|
private final LiveData<ChatColors> chatColors;
|
||||||
private final MutableLiveData<Integer> toolbarBottom;
|
private final MutableLiveData<Integer> toolbarBottom;
|
||||||
private final MutableLiveData<Integer> inlinePlayerHeight;
|
private final MutableLiveData<Integer> inlinePlayerHeight;
|
||||||
private final LiveData<Integer> scrollDateTopMargin;
|
private final LiveData<Integer> conversationTopMargin;
|
||||||
|
|
||||||
private final Map<GroupId, Set<Recipient>> sessionMemberCache = new HashMap<>();
|
private final Map<GroupId, Set<Recipient>> sessionMemberCache = new HashMap<>();
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ public class ConversationViewModel extends ViewModel {
|
||||||
this.messageInsertObserver = messageId -> pagingController.onDataItemInserted(messageId, 0);
|
this.messageInsertObserver = messageId -> pagingController.onDataItemInserted(messageId, 0);
|
||||||
this.toolbarBottom = new MutableLiveData<>();
|
this.toolbarBottom = new MutableLiveData<>();
|
||||||
this.inlinePlayerHeight = 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<Recipient> recipientLiveData = LiveDataUtil.mapAsync(recipientId, Recipient::resolved);
|
LiveData<Recipient> recipientLiveData = LiveDataUtil.mapAsync(recipientId, Recipient::resolved);
|
||||||
LiveData<ThreadAndRecipient> threadAndRecipient = LiveDataUtil.combineLatest(threadId, recipientLiveData, ThreadAndRecipient::new);
|
LiveData<ThreadAndRecipient> threadAndRecipient = LiveDataUtil.combineLatest(threadId, recipientLiveData, ThreadAndRecipient::new);
|
||||||
|
@ -161,11 +161,11 @@ public class ConversationViewModel extends ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setToolbarBottom(int bottom) {
|
void setToolbarBottom(int bottom) {
|
||||||
toolbarBottom.postValue(bottom);
|
toolbarBottom.setValue(bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInlinePlayerVisible(boolean isVisible) {
|
void setInlinePlayerVisible(boolean isVisible) {
|
||||||
inlinePlayerHeight.postValue(isVisible ? ViewUtil.dpToPx(36) : 0);
|
inlinePlayerHeight.setValue(isVisible ? ViewUtil.dpToPx(36) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onAttachmentKeyboardOpen() {
|
void onAttachmentKeyboardOpen() {
|
||||||
|
@ -186,8 +186,8 @@ public class ConversationViewModel extends ViewModel {
|
||||||
this.threadId.postValue(-1L);
|
this.threadId.postValue(-1L);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull LiveData<Integer> getScrollDateTopMargin() {
|
@NonNull LiveData<Integer> getConversationTopMargin() {
|
||||||
return scrollDateTopMargin;
|
return conversationTopMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull LiveData<Boolean> canShowAsBubble() {
|
@NonNull LiveData<Boolean> canShowAsBubble() {
|
||||||
|
|
|
@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.conversation;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.ContentObserver;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
|
@ -11,10 +10,9 @@ import androidx.lifecycle.Transformations;
|
||||||
import androidx.lifecycle.ViewModel;
|
import androidx.lifecycle.ViewModel;
|
||||||
|
|
||||||
import org.signal.core.util.concurrent.SignalExecutors;
|
import org.signal.core.util.concurrent.SignalExecutors;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders;
|
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MessageDatabase;
|
import org.thoughtcrime.securesms.database.DatabaseObserver;
|
||||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor;
|
import org.thoughtcrime.securesms.util.concurrent.SerialMonoLifoExecutor;
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
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 static final Executor EXECUTOR = new SerialMonoLifoExecutor(SignalExecutors.BOUNDED);
|
||||||
|
|
||||||
private final Application context;
|
private final Application context;
|
||||||
private final MutableLiveData<Long> threadId = new MutableLiveData<>(-1L);
|
private final MutableLiveData<Long> threadId = new MutableLiveData<>(-1L);
|
||||||
private final LiveData<Pair<Integer, Integer>> unreadCounts;
|
private final LiveData<Pair<Integer, Integer>> unreadCounts;
|
||||||
|
|
||||||
private ContentObserver observer;
|
private DatabaseObserver.Observer observer;
|
||||||
|
|
||||||
public MessageCountsViewModel() {
|
public MessageCountsViewModel() {
|
||||||
this.context = ApplicationDependencies.getApplication();
|
this.context = ApplicationDependencies.getApplication();
|
||||||
|
@ -41,18 +39,24 @@ public class MessageCountsViewModel extends ViewModel {
|
||||||
return counts;
|
return counts;
|
||||||
}
|
}
|
||||||
|
|
||||||
observer = new ContentObserver(null) {
|
observer = new DatabaseObserver.Observer() {
|
||||||
|
private int previousUnreadCount = -1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChange(boolean selfChange) {
|
public void onChanged() {
|
||||||
EXECUTOR.execute(() -> {
|
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;
|
return counts;
|
||||||
});
|
});
|
||||||
|
@ -75,19 +79,19 @@ public class MessageCountsViewModel extends ViewModel {
|
||||||
return Transformations.map(unreadCounts, Pair::second);
|
return Transformations.map(unreadCounts, Pair::second);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<Integer, Integer> getCounts(@NonNull Context context, long threadId) {
|
private int getUnreadCount(@NonNull Context context, long threadId) {
|
||||||
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
|
ThreadRecord threadRecord = DatabaseFactory.getThreadDatabase(context).getThreadRecord(threadId);
|
||||||
MessageDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context);
|
return threadRecord != null ? threadRecord.getUnreadCount() : 0;
|
||||||
int unreadCount = mmsSmsDatabase.getUnreadCount(threadId);
|
}
|
||||||
int unreadMentionCount = mmsDatabase.getUnreadMentionCount(threadId);
|
|
||||||
|
|
||||||
return new Pair<>(unreadCount, unreadMentionCount);
|
private int getUnreadMentionsCount(@NonNull Context context, long threadId) {
|
||||||
|
return DatabaseFactory.getMmsDatabase(context).getUnreadMentionCount(threadId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCleared() {
|
protected void onCleared() {
|
||||||
if (observer != null) {
|
if (observer != null) {
|
||||||
context.getContentResolver().unregisterContentObserver(observer);
|
ApplicationDependencies.getDatabaseObserver().unregisterObserver(observer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<MessageNotifierV2.StickyThread> stickyThreads) {
|
public Cursor getMessagesForNotificationState(Collection<MessageNotifierV2.StickyThread> stickyThreads) {
|
||||||
StringBuilder stickyQuery = new StringBuilder();
|
StringBuilder stickyQuery = new StringBuilder();
|
||||||
for (MessageNotifierV2.StickyThread stickyThread : stickyThreads) {
|
for (MessageNotifierV2.StickyThread stickyThread : stickyThreads) {
|
||||||
|
@ -286,13 +279,13 @@ public class MmsSmsDatabase extends Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getUnreadCount(long threadId) {
|
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);
|
Cursor cursor = queryTables(PROJECTION, selection, null, null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return cursor != null ? cursor.getCount() : 0;
|
return cursor != null ? cursor.getCount() : 0;
|
||||||
} finally {
|
} finally {
|
||||||
if (cursor != null) cursor.close();;
|
if (cursor != null) cursor.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue