Merge remote-tracking branch 'origin/libsession-integration' into libsession-integration

# Conflicts:
#	app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt
This commit is contained in:
0x330a 2023-05-11 10:46:46 +10:00
commit 7d32edb133
No known key found for this signature in database
GPG Key ID: 267811D6E6A2698C
7 changed files with 50 additions and 50 deletions

View File

@ -59,6 +59,7 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import network.loki.messenger.R import network.loki.messenger.R
@ -175,6 +176,7 @@ import org.thoughtcrime.securesms.util.toPx
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.Locale import java.util.Locale
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.atomic.AtomicLong
import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.atomic.AtomicReference
import javax.inject.Inject import javax.inject.Inject
@ -273,7 +275,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
val searchViewModel: SearchViewModel by viewModels() val searchViewModel: SearchViewModel by viewModels()
var searchViewItem: MenuItem? = null var searchViewItem: MenuItem? = null
private val bufferedLastSeenChannel = Channel<Unit>(capacity = Channel.RENDEZVOUS, onBufferOverflow = BufferOverflow.DROP_OLDEST) private val bufferedLastSeenChannel = Channel<Long>(capacity = 512, onBufferOverflow = BufferOverflow.DROP_OLDEST)
private val isScrolledToBottom: Boolean private val isScrolledToBottom: Boolean
get() { get() {
@ -341,6 +343,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
private val cameraButton by lazy { InputBarButton(this, R.drawable.ic_baseline_photo_camera_24, hasOpaqueBackground = true) } private val cameraButton by lazy { InputBarButton(this, R.drawable.ic_baseline_photo_camera_24, hasOpaqueBackground = true) }
private val messageToScrollTimestamp = AtomicLong(-1) private val messageToScrollTimestamp = AtomicLong(-1)
private val messageToScrollAuthor = AtomicReference<Address?>(null) private val messageToScrollAuthor = AtomicReference<Address?>(null)
private val firstLoad = AtomicBoolean(true)
private lateinit var reactionDelegate: ConversationReactionDelegate private lateinit var reactionDelegate: ConversationReactionDelegate
private val reactWithAnyEmojiStartPage = -1 private val reactWithAnyEmojiStartPage = -1
@ -442,16 +445,12 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) { lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
// only update the conversation every 3 seconds maximum // only update the conversation every 3 seconds maximum
// channel is rendezvous and shouldn't block on try send calls as often as we want // channel is rendezvous and shouldn't block on try send calls as often as we want
val layoutManager = binding?.conversationRecyclerView?.layoutManager as? LinearLayoutManager ?: return@repeatOnLifecycle val bufferedFlow = bufferedLastSeenChannel.consumeAsFlow()
val lastItemPos = layoutManager.findLastCompletelyVisibleItemPosition() bufferedFlow.filter {
// adapter.item it > storage.getLastSeen(viewModel.threadId)
}.collectLatest { latestMessageRead ->
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
storage.markConversationAsRead(viewModel.threadId, SnodeAPI.nowWithOffset) storage.markConversationAsRead(viewModel.threadId, latestMessageRead)
}
val bufferedFlow = bufferedLastSeenChannel.consumeAsFlow().debounce(3.seconds)
bufferedFlow.collectLatest {
withContext(Dispatchers.IO) {
storage.markConversationAsRead(viewModel.threadId, SnodeAPI.nowWithOffset)
} }
} }
} }
@ -502,8 +501,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
val author = messageToScrollAuthor.getAndSet(null) val author = messageToScrollAuthor.getAndSet(null)
if (author != null && messageTimestamp >= 0) { if (author != null && messageTimestamp >= 0) {
jumpToMessage(author, messageTimestamp, null) jumpToMessage(author, messageTimestamp, null)
} else if (firstLoad.getAndSet(false)) {
scrollToFirstUnreadMessageIfNeeded()
} }
bufferedLastSeenChannel.trySend(Unit)
} }
updatePlaceholder() updatePlaceholder()
} }
@ -998,7 +998,13 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
binding.typingIndicatorViewContainer.isVisible = wasTypingIndicatorVisibleBefore && isScrolledToBottom binding.typingIndicatorViewContainer.isVisible = wasTypingIndicatorVisibleBefore && isScrolledToBottom
binding.typingIndicatorViewContainer.isVisible binding.typingIndicatorViewContainer.isVisible
showOrHideScrollToBottomButton() showOrHideScrollToBottomButton()
val firstVisiblePosition = layoutManager?.findFirstVisibleItemPosition() ?: -1 val firstVisiblePosition = layoutManager?.findFirstCompletelyVisibleItemPosition() ?: RecyclerView.NO_POSITION
if (!firstLoad.get() && firstVisiblePosition != RecyclerView.NO_POSITION) {
val visibleItemTimestamp = adapter.getTimestampForItemAt(firstVisiblePosition)
if (visibleItemTimestamp != null) {
bufferedLastSeenChannel.trySend(visibleItemTimestamp)
}
}
unreadCount = min(unreadCount, firstVisiblePosition).coerceAtLeast(0) unreadCount = min(unreadCount, firstVisiblePosition).coerceAtLeast(0)
updateUnreadCountIndicator() updateUnreadCountIndicator()
} }

View File

@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.conversation.v2.messages.ControlMessageView
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageViewDelegate import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageViewDelegate
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
import org.thoughtcrime.securesms.database.ThreadDatabase.Reader
import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
@ -219,11 +220,14 @@ class ConversationAdapter(
fun findLastSeenItemPosition(lastSeenTimestamp: Long): Int? { fun findLastSeenItemPosition(lastSeenTimestamp: Long): Int? {
val cursor = this.cursor val cursor = this.cursor
if (lastSeenTimestamp <= 0L || cursor == null || !isActiveCursor) return null if (cursor == null || !isActiveCursor) return null
if (lastSeenTimestamp == 0L && cursor.moveToLast()) {
return cursor.position
}
for (i in 0 until itemCount) { for (i in 0 until itemCount) {
cursor.moveToPosition(i) cursor.moveToPosition(i)
val message = messageDB.readerFor(cursor).current val message = messageDB.readerFor(cursor).current
if (message.isOutgoing || message.dateReceived <= lastSeenTimestamp) { return i } if (message.isOutgoing || message.dateSent <= lastSeenTimestamp) { return i }
} }
return null return null
} }
@ -243,4 +247,11 @@ class ConversationAdapter(
this.searchQuery = query this.searchQuery = query
notifyDataSetChanged() notifyDataSetChanged()
} }
fun getTimestampForItemAt(firstVisiblePosition: Int): Long? {
val cursor = this.cursor ?: return null
if (!cursor.moveToPosition(firstVisiblePosition)) return null
val message = messageDB.readerFor(cursor).current ?: return null
return message.timestamp
}
} }

View File

@ -199,25 +199,6 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
} }
} }
@Throws(RecipientFormattingException::class, MmsException::class)
private fun getThreadIdFor(retrieved: IncomingMediaMessage): Long {
return if (retrieved.groupId != null) {
val groupRecipients = Recipient.from(
context,
retrieved.groupId,
true
)
get(context).threadDatabase().getOrCreateThreadIdFor(groupRecipients)
} else {
val sender = Recipient.from(
context,
retrieved.from,
true
)
get(context).threadDatabase().getOrCreateThreadIdFor(sender)
}
}
private fun rawQuery(where: String, arguments: Array<String>?): Cursor { private fun rawQuery(where: String, arguments: Array<String>?): Cursor {
val database = databaseHelper.readableDatabase val database = databaseHelper.readableDatabase
return database.rawQuery( return database.rawQuery(

View File

@ -64,6 +64,7 @@ import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.util.SessionMetaProtocol; import org.thoughtcrime.securesms.util.SessionMetaProtocol;
import java.io.Closeable; import java.io.Closeable;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -313,8 +314,12 @@ public class ThreadDatabase extends Database {
final List<MarkedMessageInfo> smsRecords = DatabaseComponent.get(context).smsDatabase().setMessagesRead(threadId, lastReadTime); final List<MarkedMessageInfo> smsRecords = DatabaseComponent.get(context).smsDatabase().setMessagesRead(threadId, lastReadTime);
final List<MarkedMessageInfo> mmsRecords = DatabaseComponent.get(context).mmsDatabase().setMessagesRead(threadId, lastReadTime); final List<MarkedMessageInfo> mmsRecords = DatabaseComponent.get(context).mmsDatabase().setMessagesRead(threadId, lastReadTime);
ContentValues contentValues = new ContentValues(1); if (smsRecords.isEmpty() && mmsRecords.isEmpty()) {
contentValues.put(READ, 1); return Collections.emptyList();
}
ContentValues contentValues = new ContentValues(2);
contentValues.put(READ, smsRecords.isEmpty() && mmsRecords.isEmpty());
contentValues.put(LAST_SEEN, lastReadTime); contentValues.put(LAST_SEEN, lastReadTime);
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
@ -434,9 +439,12 @@ public class ThreadDatabase extends Database {
String query = "SELECT COUNT (*) FROM " + TABLE_NAME + String query = "SELECT COUNT (*) FROM " + TABLE_NAME +
" LEFT OUTER JOIN " + RecipientDatabase.TABLE_NAME + " LEFT OUTER JOIN " + RecipientDatabase.TABLE_NAME +
" ON " + TABLE_NAME + "." + ADDRESS + " = " + RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.ADDRESS + " ON " + TABLE_NAME + "." + ADDRESS + " = " + RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.ADDRESS +
" LEFT OUTER JOIN " + GroupDatabase.TABLE_NAME +
" ON " + TABLE_NAME + "." + ADDRESS + " = " + GroupDatabase.TABLE_NAME + "." + GROUP_ID +
" WHERE " + MESSAGE_COUNT + " != 0 AND " + ARCHIVED + " = 0 AND " + HAS_SENT + " = 0 AND " + MESSAGE_COUNT + " = " + UNREAD_COUNT + " AND " + " WHERE " + MESSAGE_COUNT + " != 0 AND " + ARCHIVED + " = 0 AND " + HAS_SENT + " = 0 AND " + MESSAGE_COUNT + " = " + UNREAD_COUNT + " AND " +
RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.BLOCK + " = 0 AND " + RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.BLOCK + " = 0 AND " +
RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.APPROVED + " = 0"; RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.APPROVED + " = 0 AND " +
GroupDatabase.TABLE_NAME + "." + GROUP_ID + " IS NULL";
cursor = db.rawQuery(query, null); cursor = db.rawQuery(query, null);
if (cursor != null && cursor.moveToFirst()) if (cursor != null && cursor.moveToFirst())

View File

@ -240,10 +240,10 @@ public class DefaultMessageNotifier implements MessageNotifier {
!(recipient.isApproved() || threads.getLastSeenAndHasSent(threadId).second())) { !(recipient.isApproved() || threads.getLastSeenAndHasSent(threadId).second())) {
TextSecurePreferences.removeHasHiddenMessageRequests(context); TextSecurePreferences.removeHasHiddenMessageRequests(context);
} }
if (isVisible && recipient != null && threads.getMessageCount(threadId) > 0) { // if (isVisible && recipient != null && threads.getMessageCount(threadId) > 0) {
List<MarkedMessageInfo> messageIds = threads.setRead(threadId, true); // List<MarkedMessageInfo> messageIds = threads.setRead(threadId, false);
if (SessionMetaProtocol.shouldSendReadReceipt(recipient)) { MarkReadReceiver.process(context, messageIds); } // if (SessionMetaProtocol.shouldSendReadReceipt(recipient)) { MarkReadReceiver.process(context, messageIds); }
} // }
if (!TextSecurePreferences.isNotificationsEnabled(context) || if (!TextSecurePreferences.isNotificationsEnabled(context) ||
(recipient != null && recipient.isMuted())) (recipient != null && recipient.isMuted()))

View File

@ -70,9 +70,8 @@ fun MessageSender.create(name: String, members: Collection<String>): Promise<Str
storage.addClosedGroupPublicKey(groupPublicKey) storage.addClosedGroupPublicKey(groupPublicKey)
// Store the encryption key pair // Store the encryption key pair
storage.addClosedGroupEncryptionKeyPair(encryptionKeyPair, groupPublicKey, sentTime) storage.addClosedGroupEncryptionKeyPair(encryptionKeyPair, groupPublicKey, sentTime)
// Notify the user // Create the thread
val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID))
storage.insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.CREATION, name, members, admins, threadID, sentTime)
storage.createInitialConfigGroup(groupPublicKey, name, GroupUtil.createConfigMemberMap(members, admins), sentTime, encryptionKeyPair) storage.createInitialConfigGroup(groupPublicKey, name, GroupUtil.createConfigMemberMap(members, admins), sentTime, encryptionKeyPair)
// Notify the PN server // Notify the PN server
PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey) PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey)

View File

@ -498,13 +498,8 @@ private fun handleNewClosedGroup(sender: String, sentTimestamp: Long, groupPubli
storage.setExpirationTimer(groupID, expireTimer) storage.setExpirationTimer(groupID, expireTimer)
// Notify the PN server // Notify the PN server
PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Subscribe, groupPublicKey, storage.getUserPublicKey()!!) PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Subscribe, groupPublicKey, storage.getUserPublicKey()!!)
// Notify the user // Create thread
if (userPublicKey == sender && !groupExists) { storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID))
val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID))
storage.insertOutgoingInfoMessage(context, groupID, SignalServiceGroup.Type.CREATION, name, members, admins, threadID, sentTimestamp)
} else if (userPublicKey != sender) {
storage.insertIncomingInfoMessage(context, sender, groupID, SignalServiceGroup.Type.CREATION, name, members, admins, sentTimestamp)
}
// Start polling // Start polling
ClosedGroupPollerV2.shared.startPolling(groupPublicKey) ClosedGroupPollerV2.shared.startPolling(groupPublicKey)
} }