From 85120b57ea1101c1659f840aa415f265b66d9f9f Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 27 May 2021 15:00:16 +1000 Subject: [PATCH 1/2] Batch conversation updates --- .../securesms/ApplicationContext.java | 12 +++--- .../ConversationNotificationDebouncer.kt | 37 +++++++++++++++++++ .../securesms/database/Database.java | 10 ++--- .../securesms/database/Storage.kt | 20 ++++------ .../securesms/loki/activities/HomeActivity.kt | 9 +---- .../securesms/loki/views/ConversationView.kt | 9 +---- .../libsession/database/StorageProtocol.kt | 2 +- .../ReceivedMessageHandler.kt | 2 +- .../libsession/utilities/Debouncer.java | 1 - 9 files changed, 60 insertions(+), 42 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/ConversationNotificationDebouncer.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 4865b29a2c..0eacf377a5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -21,6 +21,7 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Handler; import android.os.Looper; + import androidx.annotation.NonNull; import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; @@ -47,7 +48,6 @@ import org.session.libsignal.utilities.ThreadUtils; import org.signal.aesgcmprovider.AesGcmProvider; import org.thoughtcrime.securesms.components.TypingStatusSender; import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule; import org.thoughtcrime.securesms.jobmanager.DependencyInjector; @@ -120,14 +120,12 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc private ProfileManager profileManager; private ObjectGraph objectGraph; private PersistentLogger persistentLogger; - - // Loki public MessageNotifier messageNotifier = null; public Poller poller = null; public Broadcaster broadcaster = null; public SignalCommunicationModule communicationModule; private Job firebaseInstanceIdJob; - private Handler threadNotificationHandler; + private Handler conversationListNotificationHandler; private volatile boolean isAppVisible; @@ -135,8 +133,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc return (ApplicationContext) context.getApplicationContext(); } - public Handler getThreadNotificationHandler() { - return this.threadNotificationHandler; + public Handler getConversationListNotificationHandler() { + return this.conversationListNotificationHandler; } @Override @@ -153,7 +151,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc AppContext.INSTANCE.configureKovenant(); messageNotifier = new OptimizedMessageNotifier(new DefaultMessageNotifier()); broadcaster = new Broadcaster(this); - threadNotificationHandler = new Handler(Looper.getMainLooper()); + conversationListNotificationHandler = new Handler(Looper.getMainLooper()); LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this); MessagingModuleConfiguration.Companion.configure(this, DatabaseFactory.getStorage(this), diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ConversationNotificationDebouncer.kt b/app/src/main/java/org/thoughtcrime/securesms/database/ConversationNotificationDebouncer.kt new file mode 100644 index 0000000000..89c61a2e63 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ConversationNotificationDebouncer.kt @@ -0,0 +1,37 @@ +package org.thoughtcrime.securesms.database + +import android.annotation.SuppressLint +import android.content.Context +import android.os.Handler +import android.os.Looper +import org.session.libsession.utilities.Debouncer +import org.thoughtcrime.securesms.ApplicationContext + +class ConversationNotificationDebouncer(private val context: Context) { + private val threadIDs = mutableSetOf() + private val handler = Handler(Looper.getMainLooper()) + private val debouncer = Debouncer(handler, 250); + + companion object { + @SuppressLint("StaticFieldLeak") + lateinit var shared: ConversationNotificationDebouncer + + fun get(context: Context): ConversationNotificationDebouncer { + if (::shared.isInitialized) { return shared } + shared = ConversationNotificationDebouncer(context) + return shared + } + } + + fun notify(threadID: Long) { + threadIDs.add(threadID) + debouncer.publish { publish() } + } + + private fun publish() { + for (threadID in threadIDs) { + context.contentResolver.notifyChange(DatabaseContentProviders.Conversation.getUriForThread(threadID), null) + } + threadIDs.clear() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Database.java b/app/src/main/java/org/thoughtcrime/securesms/database/Database.java index b1d1810a5c..3f57b1573d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Database.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Database.java @@ -35,13 +35,13 @@ public abstract class Database { protected SQLCipherOpenHelper databaseHelper; protected final Context context; - private final Debouncer threadNotificationDebouncer; + private final Debouncer conversationListNotificationDebouncer; @SuppressLint("WrongConstant") public Database(Context context, SQLCipherOpenHelper databaseHelper) { - this.context = context; + this.context = context; this.databaseHelper = databaseHelper; - this.threadNotificationDebouncer = new Debouncer(ApplicationContext.getInstance(context).getThreadNotificationHandler(), 250); + this.conversationListNotificationDebouncer = new Debouncer(ApplicationContext.getInstance(context).getConversationListNotificationHandler(), 250); } protected void notifyConversationListeners(Set threadIds) { @@ -50,11 +50,11 @@ public abstract class Database { } protected void notifyConversationListeners(long threadId) { - context.getContentResolver().notifyChange(DatabaseContentProviders.Conversation.getUriForThread(threadId), null); + ConversationNotificationDebouncer.Companion.get(context).notify(threadId); } protected void notifyConversationListListeners() { - threadNotificationDebouncer.publish(()->context.getContentResolver().notifyChange(DatabaseContentProviders.ConversationList.CONTENT_URI, null)); + conversationListNotificationDebouncer.publish(()->context.getContentResolver().notifyChange(DatabaseContentProviders.ConversationList.CONTENT_URI, null)); } protected void notifyStickerListeners() { 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 5f498fa100..b60a91c6dd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -3,8 +3,8 @@ package org.thoughtcrime.securesms.database import android.content.Context import android.net.Uri import org.session.libsession.database.StorageProtocol -import org.session.libsession.messaging.jobs.* import org.session.libsession.messaging.contacts.Contact +import org.session.libsession.messaging.jobs.* import org.session.libsession.messaging.messages.control.ConfigurationMessage import org.session.libsession.messaging.messages.signal.* import org.session.libsession.messaging.messages.signal.IncomingTextMessage @@ -16,20 +16,15 @@ import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAt import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel -import org.session.libsession.utilities.Address -import org.session.libsession.utilities.Address.Companion.fromSerialized -import org.session.libsession.utilities.GroupRecord -import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.messaging.utilities.UpdateMessageData -import org.session.libsession.utilities.GroupUtil -import org.session.libsession.utilities.IdentityKeyUtil -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsession.utilities.ProfileKeyUtil +import org.session.libsession.utilities.* +import org.session.libsession.utilities.Address.Companion.fromSerialized +import org.session.libsession.utilities.recipients.Recipient import org.session.libsignal.crypto.ecc.ECKeyPair -import org.session.libsignal.utilities.KeyHelper -import org.session.libsignal.utilities.guava.Optional import org.session.libsignal.messages.SignalServiceAttachmentPointer import org.session.libsignal.messages.SignalServiceGroup +import org.session.libsignal.utilities.KeyHelper +import org.session.libsignal.utilities.guava.Optional import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob @@ -100,7 +95,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, override fun persist(message: VisibleMessage, quotes: QuoteModel?, linkPreview: List, groupPublicKey: String?, openGroupID: String?, attachments: List): Long? { var messageID: Long? = null val senderAddress = Address.fromSerialized(message.sender!!) - val isUserSender = message.sender!! == getUserPublicKey() + val isUserSender = (message.sender!! == getUserPublicKey()) val group: Optional = when { openGroupID != null -> Optional.of(SignalServiceGroup(openGroupID.toByteArray(), SignalServiceGroup.GroupType.PUBLIC_CHAT)) groupPublicKey != null -> { @@ -120,7 +115,6 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, senderAddress } val targetRecipient = Recipient.from(context, targetAddress, false) - if (message.isMediaMessage() || attachments.isNotEmpty()) { val quote: Optional = if (quotes != null) Optional.of(quotes) else Optional.absent() val linkPreviews: Optional> = if (linkPreview.isEmpty()) Optional.absent() else Optional.of(linkPreview.mapNotNull { it!! }) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt index 0e5ccd6fde..65b34a3b0d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt @@ -51,17 +51,12 @@ import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideRequests import java.io.IOException -class HomeActivity : PassphraseRequiredActionBarActivity(), - ConversationClickListener, - SeedReminderViewDelegate, - NewConversationButtonSetViewDelegate { +class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickListener, SeedReminderViewDelegate, NewConversationButtonSetViewDelegate { private lateinit var glide: GlideRequests private var broadcastReceiver: BroadcastReceiver? = null private val publicKey: String - get() { - return TextSecurePreferences.getLocalNumber(this)!! - } + get() = TextSecurePreferences.getLocalNumber(this)!! // region Lifecycle override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt index efbaa24dbd..7ef90c0129 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/views/ConversationView.kt @@ -35,13 +35,8 @@ class ConversationView : LinearLayout { setUpViewHierarchy() } - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { - setUpViewHierarchy() - } - private fun setUpViewHierarchy() { - LayoutInflater.from(context) - .inflate(R.layout.view_conversation, this) + LayoutInflater.from(context).inflate(R.layout.view_conversation, this) } // endregion @@ -75,7 +70,7 @@ class ConversationView : LinearLayout { typingIndicatorView.visibility = if (isTyping) View.VISIBLE else View.GONE statusIndicatorImageView.visibility = View.VISIBLE when { - !thread.isOutgoing || thread.isVerificationStatusChange -> statusIndicatorImageView.visibility = View.GONE + !thread.isOutgoing -> statusIndicatorImageView.visibility = View.GONE thread.isFailed -> statusIndicatorImageView.setImageResource(R.drawable.ic_error) thread.isPending -> statusIndicatorImageView.setImageResource(R.drawable.ic_circle_dot_dot_dot) thread.isRemoteRead -> statusIndicatorImageView.setImageResource(R.drawable.ic_filled_circle_check) diff --git a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt index 00b856d809..890077be0a 100644 --- a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt @@ -12,8 +12,8 @@ import org.session.libsession.messaging.messages.visible.Attachment import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.open_groups.OpenGroupV2 import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId -import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment +import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel import org.session.libsession.utilities.Address diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt index aa347084e0..ea32099905 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt @@ -234,7 +234,7 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage, proto: SignalS } // Cancel any typing indicators if needed cancelTypingIndicatorsIfNeeded(message.sender!!) - //Notify the user if needed + // Notify the user if needed SSKEnvironment.shared.notificationManager.updateNotification(context, threadID) } //endregion diff --git a/libsession/src/main/java/org/session/libsession/utilities/Debouncer.java b/libsession/src/main/java/org/session/libsession/utilities/Debouncer.java index 84312a4c2c..1dc8a300f3 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/Debouncer.java +++ b/libsession/src/main/java/org/session/libsession/utilities/Debouncer.java @@ -12,7 +12,6 @@ import android.os.Handler; * See http://rxmarbles.com/#debounce */ public class Debouncer { - private final Handler handler; private final long threshold; From 49ee9b99914fe8bad9bb19c7a8a23c973acf0ff8 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Thu, 27 May 2021 15:39:46 +1000 Subject: [PATCH 2/2] Delete unnecessary transaction --- .../main/java/org/thoughtcrime/securesms/database/Storage.kt | 4 ---- 1 file changed, 4 deletions(-) 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 b60a91c6dd..26de5bccbb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -121,7 +121,6 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, val mmsDatabase = DatabaseFactory.getMmsDatabase(context) val insertResult = if (message.sender == getUserPublicKey()) { val mediaMessage = OutgoingMediaMessage.from(message, targetRecipient, pointerAttachments, quote.orNull(), linkPreviews.orNull()?.firstOrNull()) - mmsDatabase.beginTransaction() mmsDatabase.insertSecureDecryptedMessageOutbox(mediaMessage, message.threadID ?: -1, message.sentTimestamp!!) } else { // It seems like we have replaced SignalServiceAttachment with SessionServiceAttachment @@ -129,14 +128,11 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context, it.toSignalPointer() } val mediaMessage = IncomingMediaMessage.from(message, senderAddress, targetRecipient.expireMessages * 1000L, group, signalServiceAttachments, quote, linkPreviews) - mmsDatabase.beginTransaction() mmsDatabase.insertSecureDecryptedMessageInbox(mediaMessage, message.threadID ?: -1, message.receivedTimestamp ?: 0) } if (insertResult.isPresent) { - mmsDatabase.setTransactionSuccessful() messageID = insertResult.get().messageId } - mmsDatabase.endTransaction() } else { val smsDatabase = DatabaseFactory.getSmsDatabase(context) val isOpenGroupInvitation = (message.openGroupInvitation != null)