Sync disappear after read with other devices

This commit is contained in:
andrew
2023-10-05 02:28:33 +10:30
parent ec8b89b8a4
commit ebbe928fd2
6 changed files with 148 additions and 118 deletions

View File

@@ -816,13 +816,7 @@ public class ThreadDatabase extends Database {
MmsSmsDatabase mmsSmsDatabase = DatabaseComponent.get(context).mmsSmsDatabase();
if (mmsSmsDatabase.getConversationCount(threadId) <= 0 && !force) return false;
List<MarkedMessageInfo> messages = setRead(threadId, lastSeenTime);
if (isGroupRecipient) {
for (MarkedMessageInfo message: messages) {
MarkReadReceiver.scheduleDeletion(context, message.getExpirationInfo());
}
} else {
MarkReadReceiver.process(context, messages);
}
MarkReadReceiver.process(context, messages);
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context, threadId);
return setLastSeen(threadId, lastSeenTime);
}

View File

@@ -1,100 +0,0 @@
package org.thoughtcrime.securesms.notifications;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationManagerCompat;
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import org.session.libsession.database.StorageProtocol;
import org.session.libsession.messaging.MessagingModuleConfiguration;
import org.session.libsession.messaging.messages.control.ReadReceipt;
import org.session.libsession.messaging.sending_receiving.MessageSender;
import org.session.libsession.snode.SnodeAPI;
import org.session.libsession.utilities.Address;
import org.session.libsession.utilities.TextSecurePreferences;
import org.session.libsession.utilities.recipients.Recipient;
import org.session.libsignal.utilities.Log;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.database.MessagingDatabase.ExpirationInfo;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.util.SessionMetaProtocol;
import java.util.List;
import java.util.Map;
public class MarkReadReceiver extends BroadcastReceiver {
private static final String TAG = MarkReadReceiver.class.getSimpleName();
public static final String CLEAR_ACTION = "network.loki.securesms.notifications.CLEAR";
public static final String THREAD_IDS_EXTRA = "thread_ids";
public static final String NOTIFICATION_ID_EXTRA = "notification_id";
@SuppressLint("StaticFieldLeak")
@Override
public void onReceive(final Context context, Intent intent) {
if (!CLEAR_ACTION.equals(intent.getAction()))
return;
final long[] threadIds = intent.getLongArrayExtra(THREAD_IDS_EXTRA);
if (threadIds != null) {
NotificationManagerCompat.from(context).cancel(intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1));
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
long currentTime = SnodeAPI.getNowWithOffset();
for (long threadId : threadIds) {
Log.i(TAG, "Marking as read: " + threadId);
StorageProtocol storage = MessagingModuleConfiguration.getShared().getStorage();
storage.markConversationAsRead(threadId,currentTime, true);
}
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
public static void process(@NonNull Context context, @NonNull List<MarkedMessageInfo> markedReadMessages) {
if (markedReadMessages.isEmpty()) return;
for (MarkedMessageInfo messageInfo : markedReadMessages) {
scheduleDeletion(context, messageInfo.getExpirationInfo());
}
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) return;
Map<Address, List<SyncMessageId>> addressMap = Stream.of(markedReadMessages)
.map(MarkedMessageInfo::getSyncMessageId)
.collect(Collectors.groupingBy(SyncMessageId::getAddress));
for (Address address : addressMap.keySet()) {
List<Long> timestamps = Stream.of(addressMap.get(address)).map(SyncMessageId::getTimetamp).toList();
if (!SessionMetaProtocol.shouldSendReadReceipt(Recipient.from(context, address, false))) { continue; }
ReadReceipt readReceipt = new ReadReceipt(timestamps);
readReceipt.setSentTimestamp(SnodeAPI.getNowWithOffset());
MessageSender.send(readReceipt, address);
}
}
public static void scheduleDeletion(Context context, ExpirationInfo expirationInfo) {
if (expirationInfo.getExpiresIn() > 0 && expirationInfo.getExpireStarted() <= 0) {
ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager();
if (expirationInfo.isMms()) DatabaseComponent.get(context).mmsDatabase().markExpireStarted(expirationInfo.getId());
else DatabaseComponent.get(context).smsDatabase().markExpireStarted(expirationInfo.getId());
expirationManager.scheduleDeletion(expirationInfo.getId(), expirationInfo.isMms(), expirationInfo.getExpiresIn());
}
}
}

View File

@@ -0,0 +1,120 @@
package org.thoughtcrime.securesms.notifications
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.AsyncTask
import androidx.core.app.NotificationManagerCompat
import com.annimon.stream.Collectors
import com.annimon.stream.Stream
import nl.komponents.kovenant.task
import org.session.libsession.messaging.MessagingModuleConfiguration.Companion.shared
import org.session.libsession.messaging.messages.control.ReadReceipt
import org.session.libsession.messaging.sending_receiving.MessageSender.send
import org.session.libsession.snode.SnodeAPI
import org.session.libsession.snode.SnodeAPI.nowWithOffset
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.TextSecurePreferences.Companion.isReadReceiptsEnabled
import org.session.libsession.utilities.associateByNotNull
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.database.MessagingDatabase.ExpirationInfo
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.util.SessionMetaProtocol.shouldSendReadReceipt
class MarkReadReceiver : BroadcastReceiver() {
@SuppressLint("StaticFieldLeak")
override fun onReceive(context: Context, intent: Intent) {
if (CLEAR_ACTION != intent.action) return
val threadIds = intent.getLongArrayExtra(THREAD_IDS_EXTRA)
if (threadIds != null) {
NotificationManagerCompat.from(context)
.cancel(intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1))
object : AsyncTask<Void?, Void?, Void?>() {
override fun doInBackground(vararg params: Void?): Void? {
val currentTime = nowWithOffset
for (threadId in threadIds) {
Log.i(TAG, "Marking as read: $threadId")
val storage = shared.storage
storage.markConversationAsRead(threadId, currentTime, true)
}
return null
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
}
}
companion object {
private val TAG = MarkReadReceiver::class.java.getSimpleName()
const val CLEAR_ACTION = "network.loki.securesms.notifications.CLEAR"
const val THREAD_IDS_EXTRA = "thread_ids"
const val NOTIFICATION_ID_EXTRA = "notification_id"
@JvmStatic
fun process(context: Context, markedReadMessages: List<MarkedMessageInfo>) {
val loki = DatabaseComponent.get(context).lokiMessageDatabase()
task {
val hashToInfo = markedReadMessages.associateByNotNull { loki.getMessageServerHash(it.expirationInfo.id) }
if (hashToInfo.isEmpty()) return@task
@Suppress("UNCHECKED_CAST")
val hashToExpiry = SnodeAPI.getExpiries(hashToInfo.keys.toList(), TextSecurePreferences.getLocalNumber(context)!!)
.get()["expiries"] as Map<String, Long>
hashToInfo.forEach { (hash, info) -> hashToExpiry[hash]?.let { scheduleDeletion(context, info.expirationInfo, it - info.expirationInfo.expireStarted) } }
} fail {
Log.e(TAG, "process() disappear after read failed", it)
}
if (markedReadMessages.isEmpty()) return
for (messageInfo in markedReadMessages) {
scheduleDeletion(context, messageInfo.expirationInfo)
}
if (!isReadReceiptsEnabled(context)) return
val addressMap = Stream.of(markedReadMessages)
.map { it.syncMessageId }
.collect(Collectors.groupingBy { it.address } )
for (address in addressMap.keys) {
val timestamps = addressMap[address]!!.map { obj: SyncMessageId -> obj.timetamp }
if (!shouldSendReadReceipt(Recipient.from(context, address, false))) {
continue
}
ReadReceipt(timestamps)
.apply { sentTimestamp = nowWithOffset }
.let { send(it, address) }
}
}
fun scheduleDeletion(
context: Context?,
expirationInfo: ExpirationInfo,
expiresIn: Long = expirationInfo.expiresIn
) {
android.util.Log.d(TAG, "scheduleDeletion() called with: expirationInfo = $expirationInfo, expiresIn = $expiresIn")
if (expiresIn > 0 && expirationInfo.expireStarted <= 0) {
val expirationManager =
ApplicationContext.getInstance(context).expiringMessageManager
if (expirationInfo.isMms) DatabaseComponent.get(context!!).mmsDatabase()
.markExpireStarted(expirationInfo.id) else DatabaseComponent.get(
context!!
).smsDatabase().markExpireStarted(expirationInfo.id)
expirationManager.scheduleDeletion(
expirationInfo.id,
expirationInfo.isMms,
expiresIn
)
}
}
}
}