diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.kt index b40c049bc2..0124631907 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.kt @@ -205,7 +205,7 @@ object FullBackupImporter { db.query(ThreadDatabase.TABLE_NAME, arrayOf(ThreadDatabase.ID), ThreadDatabase.EXPIRES_IN + " > 0", null, null, null, null).use { cursor -> while (cursor != null && cursor.moveToNext()) { - DatabaseComponent.get(context).threadDatabase().update(cursor.getLong(0), false) + DatabaseComponent.get(context).threadDatabase().update(cursor.getLong(0), false, true) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index f69d19951b..e8ec3de35c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -238,6 +238,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe } val recipient = Recipient.from(this, address, false) threadId = storage.getOrCreateThreadIdFor(recipient.address) + // assume created thread + if (recipient.isContactRecipient && !recipient.isLocalNumber) { + storage.setRecipientApproved(recipient, true) // assume approved when we CREATE the thread, not send first message + } } } ?: finish() } @@ -706,7 +710,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe // region Animation & Updating override fun onModified(recipient: Recipient) { runOnUiThread { - val threadRecipient = viewModel.recipient ?: return@runOnUiThread + val threadRecipient = viewModel.recipient ?: return@runOnUiThread Log.d("Loki-DBG", "Recipient no longer here, go back?") if (threadRecipient.isContactRecipient) { binding?.blockedBanner?.isVisible = threadRecipient.isBlocked } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt index 1e5e063d55..c6ba12c087 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt @@ -186,7 +186,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa ) get(context).groupReceiptDatabase() .update(ourAddress, id, status, timestamp) - get(context).threadDatabase().update(threadId, false) + get(context).threadDatabase().update(threadId, false, true) notifyConversationListeners(threadId) } } @@ -264,7 +264,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa " WHERE " + ID + " = ?", arrayOf(id.toString() + "") ) if (threadId.isPresent) { - get(context).threadDatabase().update(threadId.get(), false) + get(context).threadDatabase().update(threadId.get(), false, true) } } @@ -651,7 +651,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa ) if (!MmsSmsColumns.Types.isExpirationTimerUpdate(mailbox)) { if (runThreadUpdate) { - get(context).threadDatabase().update(threadId, true) + get(context).threadDatabase().update(threadId, true, true) } } notifyConversationListeners(threadId) @@ -790,7 +790,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa setLastSeen(threadId) setHasSent(threadId, true) if (runThreadUpdate) { - update(threadId, true) + update(threadId, true, true) } } return messageId @@ -925,7 +925,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa groupReceiptDatabase.deleteRowsForMessage(messageId) val database = databaseHelper.writableDatabase database!!.delete(TABLE_NAME, ID_WHERE, arrayOf(messageId.toString())) - val threadDeleted = get(context).threadDatabase().update(threadId, false) + val threadDeleted = get(context).threadDatabase().update(threadId, false, true) notifyConversationListeners(threadId) notifyStickerListeners() notifyStickerPackListeners() @@ -942,7 +942,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa val database = databaseHelper.writableDatabase database!!.delete(TABLE_NAME, ID_IN, arrayOf(messageIds.joinToString(","))) - val threadDeleted = get(context).threadDatabase().update(threadId, false) + val threadDeleted = get(context).threadDatabase().update(threadId, false, true) notifyConversationListeners(threadId) notifyStickerListeners() notifyStickerPackListeners() @@ -1140,7 +1140,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa } val threadDb = get(context).threadDatabase() for (threadId in threadIds) { - val threadDeleted = threadDb.update(threadId, false) + val threadDeleted = threadDb.update(threadId, false, true) notifyConversationListeners(threadId) } notifyStickerListeners() diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index 3726d0c4a4..a1cb7fc556 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -147,7 +147,7 @@ public class SmsDatabase extends MessagingDatabase { long threadId = getThreadIdForMessage(id); - DatabaseComponent.get(context).threadDatabase().update(threadId, false); + DatabaseComponent.get(context).threadDatabase().update(threadId, false, true); notifyConversationListeners(threadId); } @@ -236,7 +236,7 @@ public class SmsDatabase extends MessagingDatabase { long threadId = getThreadIdForMessage(id); - DatabaseComponent.get(context).threadDatabase().update(threadId, false); + DatabaseComponent.get(context).threadDatabase().update(threadId, false, true); notifyConversationListeners(threadId); } @@ -299,7 +299,7 @@ public class SmsDatabase extends MessagingDatabase { ID + " = ?", new String[] {String.valueOf(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))}); - DatabaseComponent.get(context).threadDatabase().update(threadId, false); + DatabaseComponent.get(context).threadDatabase().update(threadId, false, true); notifyConversationListeners(threadId); foundMessage = true; } @@ -383,7 +383,7 @@ public class SmsDatabase extends MessagingDatabase { long threadId = getThreadIdForMessage(messageId); - DatabaseComponent.get(context).threadDatabase().update(threadId, true); + DatabaseComponent.get(context).threadDatabase().update(threadId, true, true); notifyConversationListeners(threadId); notifyConversationListListeners(); @@ -470,7 +470,7 @@ public class SmsDatabase extends MessagingDatabase { long messageId = db.insert(TABLE_NAME, null, values); if (runThreadUpdate) { - DatabaseComponent.get(context).threadDatabase().update(threadId, true); + DatabaseComponent.get(context).threadDatabase().update(threadId, true, true); } if (message.getSubscriptionId() != -1) { @@ -546,7 +546,7 @@ public class SmsDatabase extends MessagingDatabase { } if (runThreadUpdate) { - DatabaseComponent.get(context).threadDatabase().update(threadId, true); + DatabaseComponent.get(context).threadDatabase().update(threadId, true, true); } DatabaseComponent.get(context).threadDatabase().setLastSeen(threadId); @@ -595,7 +595,7 @@ public class SmsDatabase extends MessagingDatabase { SQLiteDatabase db = databaseHelper.getWritableDatabase(); long threadId = getThreadIdForMessage(messageId); db.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""}); - boolean threadDeleted = DatabaseComponent.get(context).threadDatabase().update(threadId, false); + boolean threadDeleted = DatabaseComponent.get(context).threadDatabase().update(threadId, false, true); notifyConversationListeners(threadId); return threadDeleted; } @@ -619,7 +619,7 @@ public class SmsDatabase extends MessagingDatabase { ID + " IN (" + StringUtils.join(argsArray, ',') + ")", argValues ); - boolean threadDeleted = DatabaseComponent.get(context).threadDatabase().update(threadId, false); + boolean threadDeleted = DatabaseComponent.get(context).threadDatabase().update(threadId, false, true); notifyConversationListeners(threadId); return threadDeleted; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index 86a7742710..d5e36df78a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -89,8 +89,73 @@ import org.thoughtcrime.securesms.util.SessionMetaProtocol import java.security.MessageDigest import network.loki.messenger.libsession_util.util.Contact as LibSessionContact -class Storage(context: Context, helper: SQLCipherOpenHelper, private val configFactory: ConfigFactory) : Database(context, helper), StorageProtocol { - +class Storage(context: Context, helper: SQLCipherOpenHelper, private val configFactory: ConfigFactory) : Database(context, helper), StorageProtocol, + ThreadDatabase.ConversationThreadUpdateListener { + + init { + DatabaseComponent.get(context).threadDatabase().setUpdateListener(this) + } + + // TODO: maybe add time here from formation / creation message + override fun threadCreated(address: Address, threadId: Long) { + val volatile = configFactory.convoVolatile ?: return + if (address.isGroup) { + val groups = configFactory.userGroups ?: return + if (address.isClosedGroup) { + val sessionId = GroupUtil.doubleDecodeGroupId(address.serialize()) + val legacyGroup = groups.getOrConstructLegacyGroupInfo(sessionId) + groups.set(legacyGroup) + val newVolatileParams = volatile.getOrConstructLegacyGroup(sessionId).copy( + lastRead = SnodeAPI.nowWithOffset, + ) + volatile.set(newVolatileParams) + } else if (address.isOpenGroup) { + // these should be added on the group join / group info fetch + Log.w("Loki", "Thread created called for open group address, not adding any extra information") + } + } else if (address.isContact) { + // don't update our own address into the contacts DB + if (getUserPublicKey() != address.serialize()) { + val contacts = configFactory.contacts ?: return + contacts.upsertContact(address.serialize()) + } else { + val userProfile = configFactory.user ?: return + userProfile.setNtsHidden(false) + } + val newVolatileParams = volatile.getOrConstructOneToOne(address.serialize()).copy( + lastRead = SnodeAPI.nowWithOffset + ) + volatile.set(newVolatileParams) + } + } + + override fun threadDeleted(address: Address, threadId: Long) { + val volatile = configFactory.convoVolatile ?: return + if (address.isGroup) { + val groups = configFactory.userGroups ?: return + if (address.isClosedGroup) { + val sessionId = GroupUtil.doubleDecodeGroupId(address.serialize()) + volatile.eraseLegacyClosedGroup(sessionId) + groups.eraseLegacyGroup(sessionId) + } else if (address.isOpenGroup) { + // these should be removed in the group leave / handling new configs + Log.w("Loki", "Thread delete called for open group address, expecting to be handled elsewhere") + } + } else { + volatile.eraseOneToOne(address.serialize()) + if (getUserPublicKey() != address.serialize()) { + val contacts = configFactory.contacts ?: return + contacts.upsertContact(address.serialize()) { + // hidden = true TODO: maybe this? + } + } else { + val userProfile = configFactory.user ?: return + userProfile.setNtsHidden(true) + } + } + ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context) + } + override fun getUserPublicKey(): String? { return TextSecurePreferences.getLocalNumber(context) } @@ -167,7 +232,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF override fun updateThread(threadId: Long, unarchive: Boolean) { val threadDb = DatabaseComponent.get(context).threadDatabase() - threadDb.update(threadId, unarchive) + threadDb.update(threadId, unarchive, false) } override fun persist(message: VisibleMessage, @@ -349,6 +414,16 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF profileManager.setProfileKey(context, recipient, userPic.key) setUserProfilePictureURL(userPic.url) } + if (userProfile.getNtsHidden()) { + // delete nts thread if needed + val ourThread = getThreadId(recipient) ?: return + deleteConversation(ourThread) + } else { + // create note to self thread if needed (?) + val ourThread = getOrCreateThreadIdFor(recipient.address) + setPinned(ourThread, userProfile.getNtsPriority() > 0) + } + } private fun updateContacts(contacts: Contacts) { @@ -378,15 +453,9 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF val extracted = convos.all() for (conversation in extracted) { val threadId = when (conversation) { - is Conversation.OneToOne -> conversation.sessionId.let { - getOrCreateThreadIdFor(fromSerialized(it)) - } - is Conversation.LegacyGroup -> conversation.groupId.let { - getOrCreateThreadIdFor("", it,null) - } - is Conversation.Community -> conversation.baseCommunityInfo.baseUrl.let { - getOrCreateThreadIdFor("",null, it) - } + is Conversation.OneToOne -> getOrCreateThreadIdFor(fromSerialized(conversation.sessionId)) + is Conversation.LegacyGroup -> getOrCreateThreadIdFor("", conversation.groupId,null) + is Conversation.Community -> getOrCreateThreadIdFor("",null, "${conversation.baseCommunityInfo.baseUrl}.${conversation.baseCommunityInfo.room}") } if (threadId >= 0) { markConversationAsRead(threadId, conversation.lastRead) @@ -408,8 +477,8 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF val existingCommunities: Map = allOpenGroups.filterKeys { it !in toDeleteCommunities.keys } val toAddCommunities = communities.filter { it.community.fullUrl() !in existingCommunities.map { it.value.joinURL } } - val existingJoinUrls = existingCommunities.values.map { it.joinURL } + val existingClosedGroups = getAllGroups().filter { it.isClosedGroup } val lgcIds = lgc.map { it.sessionId } val toDeleteClosedGroups = existingClosedGroups.filter { group -> @@ -837,7 +906,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF override fun setExpirationTimer(address: String, duration: Int) { val recipient = Recipient.from(context, fromSerialized(address), false) DatabaseComponent.get(context).recipientDatabase().setExpireMessages(recipient, duration) - if (recipient.isContactRecipient) { + if (recipient.isContactRecipient && !recipient.isLocalNumber) { configFactory.contacts?.upsertContact(address) { this.expiryMode = if (duration != 0) { ExpiryMode.AfterRead(duration.toLong()) @@ -1074,6 +1143,33 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF override fun setPinned(threadID: Long, isPinned: Boolean) { val threadDB = DatabaseComponent.get(context).threadDatabase() threadDB.setPinned(threadID, isPinned) + val threadRecipient = getRecipientForThread(threadID) ?: return + if (threadRecipient.isLocalNumber) { + val user = configFactory.user ?: return + user.setNtsPriority(if (isPinned) 1 else 0) + } else if (threadRecipient.isContactRecipient) { + val contacts = configFactory.contacts ?: return + contacts.upsertContact(threadRecipient.address.serialize()) { + priority = if (isPinned) 1 else 0 + } + } else if (threadRecipient.isGroupRecipient) { + val groups = configFactory.userGroups ?: return + if (threadRecipient.isClosedGroupRecipient) { + val sessionId = GroupUtil.doubleDecodeGroupId(threadRecipient.address.serialize()) + val newGroupInfo = groups.getOrConstructLegacyGroupInfo(sessionId).copy ( + priority = if (isPinned) 1 else 0 + ) + groups.set(newGroupInfo) + } else if (threadRecipient.isOpenGroupRecipient) { + val openGroup = getOpenGroup(threadID) ?: return + val (baseUrl, room, pubKeyHex) = BaseCommunityInfo.parseFullUrl(openGroup.joinURL) ?: return + val newGroupInfo = groups.getOrConstructCommunityInfo(baseUrl, room, Hex.toStringCondensed(pubKeyHex)).copy ( + priority = if (isPinned) 1 else 0 + ) + groups.set(newGroupInfo) + } + } + ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context) } override fun isPinned(threadID: Long): Boolean { @@ -1234,6 +1330,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF override fun setRecipientApproved(recipient: Recipient, approved: Boolean) { DatabaseComponent.get(context).recipientDatabase().setApproved(recipient, approved) + if (recipient.isLocalNumber || !recipient.isContactRecipient) return configFactory.contacts?.upsertContact(recipient.address.serialize()) { this.approved = approved } @@ -1241,6 +1338,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF override fun setRecipientApprovedMe(recipient: Recipient, approvedMe: Boolean) { DatabaseComponent.get(context).recipientDatabase().setApprovedMe(recipient, approvedMe) + if (recipient.isLocalNumber || !recipient.isContactRecipient) return configFactory.contacts?.upsertContact(recipient.address.serialize()) { this.approvedMe = approvedMe } @@ -1376,7 +1474,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper, private val configF override fun setBlocked(recipients: List, isBlocked: Boolean, fromConfigUpdate: Boolean) { val recipientDb = DatabaseComponent.get(context).recipientDatabase() recipientDb.setBlocked(recipients, isBlocked) - recipients.filter { it.isContactRecipient }.forEach { recipient -> + recipients.filter { it.isContactRecipient && !it.isLocalNumber }.forEach { recipient -> configFactory.contacts?.upsertContact(recipient.address.serialize()) { this.blocked = isBlocked } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java index 1e3ab9e895..b1e1b54af9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -72,6 +72,11 @@ import java.util.Set; public class ThreadDatabase extends Database { + public interface ConversationThreadUpdateListener { + public void threadCreated(@NonNull Address address, long threadId); + public void threadDeleted(@NonNull Address address, long threadId); + } + private static final String TAG = ThreadDatabase.class.getSimpleName(); private final Map addressCache = new HashMap<>(); @@ -139,10 +144,16 @@ public class ThreadDatabase extends Database { "ADD COLUMN " + UNREAD_MENTION_COUNT + " INTEGER DEFAULT 0;"; } + private ConversationThreadUpdateListener updateListener; + public ThreadDatabase(Context context, SQLCipherOpenHelper databaseHelper) { super(context, databaseHelper); } + public void setUpdateListener(ConversationThreadUpdateListener updateListener) { + this.updateListener = updateListener; + } + private long createThreadForRecipient(Address address, boolean group, int distributionType) { ContentValues contentValues = new ContentValues(4); long date = System.currentTimeMillis(); @@ -205,10 +216,14 @@ public class ThreadDatabase extends Database { } private void deleteThread(long threadId) { + Recipient recipient = getRecipientForThreadId(threadId); SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.delete(TABLE_NAME, ID_WHERE, new String[] {threadId + ""}); + int numberRemoved = db.delete(TABLE_NAME, ID_WHERE, new String[] {threadId + ""}); addressCache.remove(threadId); notifyConversationListListeners(); + if (updateListener != null && numberRemoved > 0 && recipient != null) { + updateListener.threadDeleted(recipient.getAddress(), threadId); + } } private void deleteThreads(Set threadIds) { @@ -276,7 +291,7 @@ public class ThreadDatabase extends Database { DatabaseComponent.get(context).smsDatabase().deleteMessagesInThreadBeforeDate(threadId, lastTweetDate); DatabaseComponent.get(context).mmsDatabase().deleteMessagesInThreadBeforeDate(threadId, lastTweetDate); - update(threadId, false); + update(threadId, false, true); notifyConversationListeners(threadId); } } finally { @@ -289,7 +304,7 @@ public class ThreadDatabase extends Database { Log.i("ThreadDatabase", "Trimming thread: " + threadId + " before :"+timestamp); DatabaseComponent.get(context).smsDatabase().deleteMessagesInThreadBeforeDate(threadId, timestamp); DatabaseComponent.get(context).mmsDatabase().deleteMessagesInThreadBeforeDate(threadId, timestamp); - update(threadId, false); + update(threadId, false, true); notifyConversationListeners(threadId); } @@ -475,7 +490,7 @@ public class ThreadDatabase extends Database { } public Cursor getApprovedConversationList() { - String where = "((" + MESSAGE_COUNT + " != 0 AND (" + HAS_SENT + " = 1 OR " + RecipientDatabase.APPROVED + " = 1 OR "+ GroupDatabase.TABLE_NAME +"."+GROUP_ID+" LIKE '"+CLOSED_GROUP_PREFIX+"%')) OR " + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " LIKE '" + OPEN_GROUP_PREFIX + "%') " + + String where = "((" + HAS_SENT + " = 1 OR " + RecipientDatabase.APPROVED + " = 1 OR "+ GroupDatabase.TABLE_NAME +"."+GROUP_ID+" LIKE '"+CLOSED_GROUP_PREFIX+"%') OR " + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " LIKE '" + OPEN_GROUP_PREFIX + "%') " + "AND " + ARCHIVED + " = 0 "; return getConversationList(where); } @@ -640,13 +655,19 @@ public class ThreadDatabase extends Database { try { cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null); - + long threadId; + boolean created = false; if (cursor != null && cursor.moveToFirst()) { - return cursor.getLong(cursor.getColumnIndexOrThrow(ID)); + threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ID)); } else { DatabaseComponent.get(context).recipientDatabase().setProfileSharing(recipient, true); - return createThreadForRecipient(recipient.getAddress(), recipient.isGroupRecipient(), distributionType); + threadId = createThreadForRecipient(recipient.getAddress(), recipient.isGroupRecipient(), distributionType); + created = true; } + if (created && updateListener != null) { + updateListener.threadCreated(recipient.getAddress(), threadId); + } + return threadId; } finally { if (cursor != null) cursor.close(); @@ -687,11 +708,11 @@ public class ThreadDatabase extends Database { notifyConversationListeners(threadId); } - public boolean update(long threadId, boolean unarchive) { + public boolean update(long threadId, boolean unarchive, boolean shouldDeleteOnEmpty) { MmsSmsDatabase mmsSmsDatabase = DatabaseComponent.get(context).mmsSmsDatabase(); long count = mmsSmsDatabase.getConversationCount(threadId); - boolean shouldDeleteEmptyThread = deleteThreadOnEmpty(threadId); + boolean shouldDeleteEmptyThread = !shouldDeleteOnEmpty ? false : deleteThreadOnEmpty(threadId); if (count == 0 && shouldDeleteEmptyThread) { deleteThread(threadId); diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt index 121afd7f01..4e2fc9f37e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -573,7 +573,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), private fun setConversationPinned(threadId: Long, pinned: Boolean) { lifecycleScope.launch(Dispatchers.IO) { - threadDb.setPinned(threadId, pinned) + storage.setPinned(threadId, pinned) homeViewModel.tryUpdateChannel() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt index ca74da5548..418a9e00f2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ConfigurationMessageUtilities.kt @@ -22,28 +22,40 @@ import org.session.libsession.messaging.utilities.SessionId import org.session.libsession.utilities.Address import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsession.utilities.WindowDebouncer import org.session.libsignal.utilities.Hex import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.toHexString import org.thoughtcrime.securesms.dependencies.DatabaseComponent +import java.util.* object ConfigurationMessageUtilities { + val debouncer = WindowDebouncer(3000, Timer()) + + private fun scheduleConfigSync(userPublicKey: String) { + debouncer.publish { + // don't schedule job if we already have one + val storage = MessagingModuleConfiguration.shared.storage + val ourDestination = Destination.Contact(userPublicKey) + val currentStorageJob = storage.getConfigSyncJob(ourDestination) + if (currentStorageJob != null) { + (currentStorageJob as ConfigurationSyncJob).shouldRunAgain.set(true) + Log.d("Loki-DBG", "Not scheduling another one") + return@publish + } + val newConfigSync = ConfigurationSyncJob(ourDestination) + Log.d("Loki", "Scheduling new ConfigurationSyncJob") + JobQueue.shared.add(newConfigSync) + } + } + @JvmStatic fun syncConfigurationIfNeeded(context: Context) { // add if check here to schedule new config job process and return early val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return - val storage = MessagingModuleConfiguration.shared.storage if (ConfigBase.isNewConfigEnabled) { - // don't schedule job if we already have one - val ourDestination = Destination.Contact(userPublicKey) - if (storage.getConfigSyncJob(ourDestination) != null) { - Log.d("Loki", "ConfigSyncJob is already running for our destination") - return - } - val newConfigSync = ConfigurationSyncJob(ourDestination) - Log.d("Loki", "Scheduling new ConfigurationSyncJob") - JobQueue.shared.add(newConfigSync) + scheduleConfigSync(userPublicKey) return } val lastSyncTime = TextSecurePreferences.getLastConfigurationSyncTime(context) @@ -70,22 +82,11 @@ object ConfigurationMessageUtilities { fun forceSyncConfigurationNowIfNeeded(context: Context): Promise { // add if check here to schedule new config job process and return early val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return Promise.ofFail(NullPointerException("User Public Key is null")) - val storage = MessagingModuleConfiguration.shared.storage if (ConfigBase.isNewConfigEnabled) { // schedule job if none exist // don't schedule job if we already have one Log.d("Loki-DBG", "Forcing config sync") - val ourDestination = Destination.Contact(userPublicKey) - val currentStorageJob = storage.getConfigSyncJob(ourDestination) - if (currentStorageJob != null) { - (currentStorageJob as ConfigurationSyncJob).shouldRunAgain.set(true) - Log.d("Loki-DBG", "Not scheduling another one") - return Promise.ofFail(NullPointerException("A job is already pending or in progress, don't schedule another job")) - } - val newConfigSync = ConfigurationSyncJob(ourDestination) - Log.d("Loki", "Scheduling new ConfigurationSyncJob") - JobQueue.shared.add(newConfigSync) - // treat this promise as succeeding now (so app continues running and doesn't block UI) + scheduleConfigSync(userPublicKey) return Promise.ofSuccess(Unit) } val contacts = ContactUtilities.getAllContacts(context).filter { recipient -> diff --git a/libsession-util/src/main/cpp/user_profile.cpp b/libsession-util/src/main/cpp/user_profile.cpp index 075553c7dd..1b8426d75c 100644 --- a/libsession-util/src/main/cpp/user_profile.cpp +++ b/libsession-util/src/main/cpp/user_profile.cpp @@ -89,4 +89,16 @@ Java_network_loki_messenger_libsession_1util_UserProfile_setNtsHidden(JNIEnv *en jboolean is_hidden) { auto profile = ptrToProfile(env, thiz); profile->set_nts_hidden(is_hidden); +} +extern "C" +JNIEXPORT jint JNICALL +Java_network_loki_messenger_libsession_1util_UserProfile_getNtsPriority(JNIEnv *env, jobject thiz) { + auto profile = ptrToProfile(env, thiz); + return profile->get_nts_priority(); +} +extern "C" +JNIEXPORT jboolean JNICALL +Java_network_loki_messenger_libsession_1util_UserProfile_getNtsHidden(JNIEnv *env, jobject thiz) { + auto profile = ptrToProfile(env, thiz); + return profile->get_nts_hidden(); } \ No newline at end of file diff --git a/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt b/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt index a9eec18a9f..88d4737846 100644 --- a/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt +++ b/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt @@ -64,7 +64,7 @@ class Contacts(pointer: Long) : ConfigBase(pointer) { /** * Similar to [updateIfExists], but will create the underlying contact if it doesn't exist before passing to [updateFunction] */ - fun upsertContact(sessionId: String, updateFunction: Contact.()->Unit) { + fun upsertContact(sessionId: String, updateFunction: Contact.()->Unit = {}) { val contact = getOrConstruct(sessionId) updateFunction(contact) set(contact) @@ -95,7 +95,9 @@ class UserProfile(pointer: Long) : ConfigBase(pointer) { external fun getPic(): UserPic external fun setPic(userPic: UserPic) external fun setNtsPriority(priority: Int) + external fun getNtsPriority(): Int external fun setNtsHidden(isHidden: Boolean) + external fun getNtsHidden(): Boolean } class ConversationVolatileConfig(pointer: Long): ConfigBase(pointer) { diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPoller.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPoller.kt index 562ddda699..4c53adc562 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPoller.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPoller.kt @@ -86,7 +86,9 @@ class OpenGroupPoller(private val server: String, private val executorService: S isCaughtUp = true } } - executorService?.schedule(this@OpenGroupPoller::poll, pollInterval, TimeUnit.MILLISECONDS) + if (hasStarted) { + executorService?.schedule(this@OpenGroupPoller::poll, pollInterval, TimeUnit.MILLISECONDS) + } }.fail { updateCapabilitiesIfNeeded(isPostCapabilitiesRetry, it) }.map { } @@ -115,6 +117,7 @@ class OpenGroupPoller(private val server: String, private val executorService: S roomToken: String, pollInfo: OpenGroupApi.RoomPollInfo ) { + if (!hasStarted) return val storage = MessagingModuleConfiguration.shared.storage val groupId = "$server.$roomToken" val dbGroupId = GroupUtil.getEncodedOpenGroupID(groupId.toByteArray())